#include <AudioPlayer.hpp>
#include <foundation.hpp>
#include <LvDisp.hpp>
#include <InteractApp.hpp>
extern "C" {
#include <lvgl.h>
#include "collections_c/array.h"
#include <font_default.h>
#include <ui_xml.h>
}

#define FONT_PATH "demo/icon/"

static long long get_tick(void)
{
    long long rev;
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    rev = ts.tv_sec * 1000ll + ts.tv_nsec / 1000000;
    return rev;
}

static lv_obj_t *makeButton(lv_obj_t *par, const char *title) {
    lv_obj_t *btn = lv_btn_create(par, NULL);
    lv_obj_t *label = lv_label_create(btn, NULL);
	lv_font_t *font = font_default_get(16);
	lv_obj_set_style_local_text_font(
		label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
    lv_label_set_text(label, title);
    lv_obj_set_size(btn, lv_obj_get_width(label) + 10, 30);

    lv_obj_set_style_local_radius(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 1);
    return btn;
}

static lv_obj_t *createLabel(lv_obj_t *par, const char *title = nullptr)
{
	lv_font_t *font = font_default_get(16);
	lv_obj_t *label = lv_label_create(par, NULL);
	lv_obj_set_style_local_text_font(
		label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font);
    if (title) lv_label_set_text(label, title);
	return label;
}


class Card 
{
public:
    lv_obj_t *card;
    lv_obj_t *titleLabel;
    lv_obj_t *detailLabel;
    lv_obj_t *addrLabel;
    lv_obj_t *connButton;

    Card(lv_obj_t *par) {
        card = lv_obj_create(par, NULL);
        lv_obj_set_style_local_bg_color(
			card, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xe1e1e1));

        titleLabel = createLabel(card);
        lv_obj_set_style_local_text_font(
            titleLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_default_get(20));
        detailLabel = createLabel(card);
        addrLabel = createLabel(card);
        lv_label_set_text(addrLabel, "AA:BB:CC:DD:EE:FF");

        connButton = makeButton(card, "连接");
        lv_obj_set_size(connButton, 100, 40);
    }

    void resize() {
        lv_obj_align(titleLabel, NULL, LV_ALIGN_IN_TOP_LEFT, 10, 25);
        lv_obj_align(detailLabel, NULL, LV_ALIGN_IN_TOP_RIGHT, -10, 25);
        lv_obj_align(addrLabel, titleLabel, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 10);
        lv_obj_align(connButton, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -10, -10);
    }
    ~Card() {
        lv_obj_del(card);
    }
};

static bool pathIsDir(const char *path)
{  
    int rev;
    struct stat stat_;
    rev = stat(path, &stat_);
    printf("stat rev:%d\n", rev);
    return !S_ISREG(stat_.st_mode);
}

static Array *loadFileList(const char *path)
{
	Array *array;
	array_new(&array);

    DIR *dir = opendir(path);
    if (dir == NULL) {
        printf("open dir error:%d %s\n", errno, path);
        return array;
    }
    while (1) {
        struct dirent *item = readdir(dir);
        if (item == NULL)
            break;
        //if (item->d_type != DT_REG)
		//  continue;
//        if (strstr(item->d_name, ".mp3") == item->d_name + strlen(item->d_name) - 4)
		{
			array_add(array, strdup(item->d_name));
        }
    }

	return array;
}

class SongListItem;
typedef std::function<void(SongListItem *item)> SongListItemClickCB;

class SongListItem
{
public:
	lv_obj_t *base;
	lv_obj_t *titleLabel;
    SongListItemClickCB cb = nullptr;
	SongListItem(lv_obj_t *par, const char *title) {
		base = lv_obj_create(par, NULL);
		titleLabel = createLabel(base, title);
        lv_page_glue_obj(base, true);
        lv_obj_set_click(base, true);

        lv_obj_set_event_cb(base, [](lv_obj_t *obj, lv_event_t event) {
            SongListItem *window = (SongListItem *)lv_obj_get_user_data(obj);
            window->lvEvent(obj, event);
        });
        lv_obj_set_user_data(base, (void *)this);
	}
	void setPosition(int x, int y, int w, int h) {
		lv_obj_set_size(base, w, h);
		lv_obj_set_pos(base,  x, y);
		lv_obj_align(titleLabel, NULL, LV_ALIGN_IN_LEFT_MID, 10, 0);
	}
    void lvEvent(lv_obj_t *obj, lv_event_t event) {
        if (obj == base && event == LV_EVENT_SHORT_CLICKED) {
            if (cb) cb(this);
            return;
        }
    }
	~SongListItem() {
		lv_obj_del(base);
	}
};

class SongList;

typedef std::function<void(SongList *item, Array *list, int id)> SongListClickCB;

class SongList
{
public:
    SongListClickCB cb = nullptr;
	lv_obj_t *base;
	Array *list = nullptr;
	Array *items = nullptr;
    char *path = nullptr;
	SongList(lv_obj_t *par, const char *path) {
		base = lv_page_create(par, NULL);
        lv_obj_set_style_local_pad_all(base, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);

		showList(path);
		hidden();
	}

    void showList(const char *path) {
		int i;
        if (list) {
            for (i = 0; i < array_size(list); i++) {
                SongListItem *item = (SongListItem *)array_get_for(items, i);
                delete item;
                char *files = (char *)array_get_for(list, i);
                free(files);
            }
            array_destroy(list);
            array_destroy(items);
            list = nullptr;
            items = nullptr;
        }
        if (this->path) {
            free(this->path);
            this->path = nullptr;
        }

        this->path = strdup(path);
        list = loadFileList(path);

		array_new(&items);
		for (i = 0; i < array_size(list); i++) {
			char *title = (char *)array_get_for(list, i);
			SongListItem *item = new SongListItem(base, title);
			array_add(items, item);

            item->cb = [this, i, title](SongListItem *item) {

                char newpath[256];
                snprintf(newpath, sizeof(newpath), "%s/%s", this->path, title);
                printf("item click:%d path:%s\n", i, newpath);
                if (pathIsDir(newpath))
                    showList(newpath);
                else {
                    if (cb) {
                        Array *fileList;
                        array_new(&fileList);
                        int j;
                        int clickId = 0;
                        for (j = 0; j < array_size(list); j++) {
                            array_add(fileList, strdup((char*)array_get_for(list, j)));
                            if (j < i) {
                                clickId++;
                            }
                        }
                        cb(this, fileList, clickId);
                    }
                }
            };
		}
        setPosition(lv_obj_get_x(base),lv_obj_get_y(base),
                lv_obj_get_width(base),lv_obj_get_height(base));
    }

    bool isHidden() {return lv_obj_get_hidden(base);}
	void show() {
		lv_obj_set_hidden(base, false);
	}
	void hidden() {
		lv_obj_set_hidden(base, true);
	}
	void setPosition(int x, int y, int w, int h) {
		int i;
		lv_obj_set_size(base, w, h);

		for (i = 0; i < array_size(list); i++) {
			SongListItem *item = (SongListItem *)array_get_for(items, i);
			item->setPosition(0, 30 * i, w, 30);
		}
	}
	~SongList() {
		int i;
		lv_obj_del(base);
		for (i = 0; i < array_size(list); i++) {
			SongListItem *item = (SongListItem *)array_get_for(items, i);
			delete item;
			char *files = (char *)array_get_for(list, i);
			free(files);
		}
	}
};

static void btn_set_title(lv_obj_t *btn, const char *title)
{
    lv_obj_t *label = lv_obj_get_child(btn, NULL);
    lv_label_set_text(label, title);
}

class MainWindow : public LvDispWin
{
public:
    const Array *adapters = nullptr;
    lv_obj_t *dd;
    lv_obj_t *scanBtn, *powerBtn, *noAdapter;
    lv_obj_t *page;

    Array *cards = nullptr;
    lv_obj_t *titleLabel;
    lv_obj_t *playPic, *prevPic, *nextPic;
    lv_obj_t *listPic;

    SongList *list;
    AudioPlayer *player = nullptr;
    bool started = false;
    Array *songList;
    int songId;
public:
    MainWindow() : LvDispWin(nullptr) {
        lv_disp_t *disp = this->disp;
        lv_obj_t *scr = lv_disp_get_scr_act(disp);

		lv_font_t *font = font_default_get(20);


        titleLabel = createLabel(scr, "刘德华-今天.mp3");
	    lv_obj_set_style_local_text_font(
		    titleLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font_default_get(32));

        lv_pic_data_src_t *src;

        playPic = lv_pic_create(scr, NULL);
        src = pic_util_load_from_file(FONT_PATH"ic_play.png");
        lv_pic_set_src_stat(playPic, LV_PIC_STATE_RELEASED, src);
        src = pic_util_load_from_file(FONT_PATH"ic_pause.png");
        lv_pic_set_src_stat(playPic, LV_PIC_STATE_CHECKED_RELEASED, src);
        printf("png size:%dx%d\n", src->w, src->h);


        prevPic = lv_pic_create(scr, NULL);
        src = pic_util_load_from_file(FONT_PATH"ic_prev.png");
        lv_pic_set_src(prevPic, src);

        nextPic = lv_pic_create(scr, NULL);
        src = pic_util_load_from_file(FONT_PATH"ic_next.png");
        lv_pic_set_src(nextPic, src);
        
        listPic = lv_pic_create(scr, NULL);
        src = pic_util_load_from_file(FONT_PATH"ic_list.png");
        lv_pic_set_src(listPic, src);

		list = new SongList(scr, ".");
		list->setPosition(10, 10, lv_obj_get_height(scr), 300);
		list->show();
		lv_obj_align(list->base, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 10, 0);
        list->cb = [this](SongList *l, Array *list, int id) {
            play(list, id);
        };
        //lv_obj_t *sw = lv_switch_create(scr, NULL);
        //lv_obj_align(sw, powerBtn, LV_ALIGN_OUT_RIGHT_MID, 20, 0);

        bindLvEvent(playPic);
        bindLvEvent(prevPic);
        bindLvEvent(nextPic);
        bindLvEvent(listPic);
    
        updateAllState();
        updateLayout();
    }

    void play(Array *list, int id) {
        char *mp3Path = (char *)array_get_for(list, id);
        printf("click id:%d\n", id);
        printf("click file:%s\n", mp3Path);

        if (player) {
            player->destroy();
            player = nullptr;
        }
        if (songList) {
            array_destroy_cb(songList, free);
            songList = nullptr;
            songId = 0;
        }
        songList = list;
        songId = id;
        FRunLoop *runLoop = InteractApp::getGlobal()->getRunLoop();
        player = new AudioPlayer(runLoop, mp3Path);
        player->stateChangeCB = [this](AudioPlayer *player) {
            audioPlayerStateChange();
        };
        player->load();
        started = false;
    }

    void audioPlayerStateChange() {
        lv_obj_set_click(playPic, true);
        if (player == nullptr) {
            lv_obj_set_click(playPic, false);
            return;
        }
        AudioPlayer::State state = player->getState();
        printf("state change:%d\n", state);
        switch (state) {
            case AudioPlayer::State::IDLE:
            lv_obj_set_click(playPic, false);
            break;
            case AudioPlayer::State::LOADING:
            lv_obj_set_click(playPic, false);
            break;
            case AudioPlayer::State::PAUSED:
            lv_pic_set_stat(playPic, LV_PIC_STATE_RELEASED);
                printf("paused\n");
            if (!started) {
                started = true;
                player->resume();
                printf("resume\n");
                lv_pic_set_stat(playPic, LV_PIC_STATE_CHECKED_RELEASED);
            }
            break;
            case AudioPlayer::State::PLAYING:
            lv_obj_set_click(playPic, false);
            lv_pic_set_stat(playPic, LV_PIC_STATE_CHECKED_RELEASED);
            break;
        }

    }

    void updateLayout() {
        lv_obj_t *scr = lv_disp_get_scr_act(disp);
        
        lv_obj_align(titleLabel, NULL, LV_ALIGN_IN_TOP_MID, 0, 40);
        lv_obj_align(playPic, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -10);
        lv_obj_align(prevPic, playPic, LV_ALIGN_OUT_LEFT_MID, -10, 0);
        lv_obj_align(nextPic, playPic, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
        lv_obj_align(listPic, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 10, -10);

		lv_obj_align(list->base, NULL, LV_ALIGN_IN_BOTTOM_LEFT, 10, -35);
        list->setPosition(10, 10, 500, lv_obj_get_height(scr) - 35);
        static int i = 0;
        printf("uupdate layout :%d\n", i++);
    }

    void updateAllState() {
        int i;
    }

    void lvEvent(lv_obj_t *obj, lv_event_t event) {
        int i;
        lv_obj_t *scr = lv_disp_get_scr_act(disp);
        if (obj == playPic && event == LV_EVENT_SHORT_CLICKED) {
            if (player == nullptr) return;
            AudioPlayer::State state = player->getState();
            if (state == AudioPlayer::State::PAUSED) {
                player->resume();
                lv_pic_set_stat(obj, LV_PIC_STATE_CHECKED_RELEASED);
            }
            if (state == AudioPlayer::State::PLAYING) {
                player->pause();
                lv_pic_set_stat(obj, LV_PIC_STATE_RELEASED);
            }
            return;
        }
        if (obj == prevPic && event == LV_EVENT_SHORT_CLICKED) {
            
            return;
        }
        if (obj == nextPic && event == LV_EVENT_SHORT_CLICKED) {
            
            return;
        }
        if (obj == listPic && event == LV_EVENT_SHORT_CLICKED) {
            if (list->isHidden()) {
                list->show();
            } else {
                list->hidden();
            }
            return;
        }
    }

    int objIsCardId(lv_obj_t *obj) {
        int i = 0;
        for (i = 0; i < array_size(cards); i++) {
            Card *car = (Card *)array_get_for(cards, i);
            if (car->card == obj) return i;
        }
        return -1;
    }
    int objIsCardConnectBtn(lv_obj_t *obj) {
        int i = 0;
        for (i = 0; i < array_size(cards); i++) {
            Card *car = (Card *)array_get_for(cards, i);
            if (car->connButton == obj) return i;
        }
        return -1;
    }

    void bindLvEvent(lv_obj_t *dd, void *userdata = nullptr) {
        lv_obj_set_event_cb(dd, [](lv_obj_t *obj, lv_event_t event) {
            MainWindow *window = (MainWindow *)lv_obj_get_user_data(obj);
            window->lvEvent(obj, event);
        });
        lv_obj_set_user_data(dd, (void *)userdata?:this);
    }

    void showAdapter(int id) {
        
        updateLayout();
    }
    
    virtual void onSizeChange(int w, int h) {
        LvDispWin::onSizeChange(w, h);
        updateLayout();
    }
};

#include "signal.h"

static void sig_(int signo)
{
    exit(0);
}

int main()
{
    signal(SIGINT, sig_);
    int i;
    FRunLoop *loop = new FRunLoop();
    loop->setIntervalMs(20);
    InteractApp *iapp = new InteractApp(loop);

    lv_init();
    LvDispApp *app = new LvDispApp(loop);
    LvDispWin *win = new MainWindow();
    win->resize(640, 480);
    win->show();

    while (true) {

        loop->runOnce();
        lv_tick_inc(get_tick() - lv_tick_get());
        lv_task_handler();
        if (loop->isInterrupt()) break;
    }
    return 0;
}
